package main

import (
	"fmt"
	"time"
	"sync"
)



func main() {
	
	// for i:=0;i<100;i++{
	// 	go add(10)
	// }
	// for i:=0;i<10;i++{
	// 	go fmt.Println("和为：",readSum())
	// }
	// // 防止提前退出
	// time.Sleep(2*time.Second)

	// fmt.Println("和为：",sum) // sum 不是并发安全的
	// run()
	// doOnce()
	// race()

	var wg sync.WaitGroup
	wg.Add(1)
	stopCh := make(chan bool)// 停止监控狗
	go func(){
		defer wg.Done()
		watchDog("【监控狗1】",stopCh)
	}()
	time.Sleep(5*time.Second)// 先让监控狗监控5秒
	stopCh <- true // 发送停止指令
	wg.Wait()
}
// var sum = 0
// func add(i int){
// 	sum += i
// }


// sync.Mutex 互斥锁 sync.RWMutex 读写锁
var(
	sum int
	// mutex sync.Mutex
	mutex sync.RWMutex
)
// Lock 和 Unlock 方法总是成对出现 一定要执行释放嗦unlock defer 语句
func add(i int){
	mutex.Lock()
	sum += i
	defer mutex.Unlock()
}

func readSum() int {
	// mutex.Lock()
	// defer mutex.Unlock()
	mutex.RLock()
	defer mutex.RUnlock()
	b:=sum
	return b
}
// 监听协程 sync.WaitGroup
func run(){
	var wg sync.WaitGroup
	wg.Add(110) // 监控110个协程 计数器值110
	for i:=0;i<100;i++{
		go func(){
			defer wg.Done() // 计数器值-1
			add(10)
		}()
	}
	for i:=0; i<10;i++ {
		go func(){
			defer wg.Done() // 计数器值-1
			fmt.Println("和为:",readSum())
		}()
	 }
	// 一直等待。计数器值为0
	wg.Wait()
}
// 程序只执行一次sync.Once
func doOnce(){
	var once sync.Once
	onceBody := func(){
		fmt.Println("Only once")
	}
	// 用于等待协程执行完毕
	done := make(chan bool)
	for i:=0;i<10;i++{
		go func(){
			once.Do(onceBody)
			done <- true
		}()
	}
	for i:=0;i<10;i++{
		<-done
	}
}

// 协程的唤醒和等待
func race(){
	cond := sync.NewCond(&sync.Mutex{})
	var wg sync.WaitGroup
	wg.Add(11)
	for i:=0;i<10;i++{
		go func(num int){
			defer wg.Done()
			fmt.Println(num,"号已经就位")
			cond.L.Lock()
			cond.Wait()
			fmt.Println(num,"号开始跑……")
         	cond.L.Unlock()
		}(i)
	}
	time.Sleep(2*time.Second)
	go func() {
		defer  wg.Done()
		fmt.Println("裁判已经就位，准备发令枪")
		fmt.Println("比赛开始，大家准备跑")
		cond.Broadcast()//发令枪响
	 }()
	 //防止函数提前返回退出
	 wg.Wait()
}

// 监控狗
func watchDog(name string,stopCh chan bool){
	// 开启for select循环，一直后台监控
	for{
		select{
		case <-stopCh:
			fmt.Println(name,"停止指令收到，马上停止")
			return
		default:
			fmt.Println(name,"正在监控......")
		}
		time.Sleep(1*time.Second)
	}
}
/*
	从使用上讲，Go 语言还是更推荐 channel 这种更高级别的并发控制方式，
	因为它更简洁，也更容易理解和使用。
*/